package com.zzyl.nursing.service.impl;

import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.framework.security.handle.LogoutSuccessHandlerImpl;
import com.zzyl.nursing.config.WebSocketServer;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.system.mapper.SysUserRoleMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.web.header.writers.CacheControlHeadersWriter;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.service.IAlertRuleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.transaction.annotation.Transactional;
import oshi.software.os.OperatingSystem;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 报警规则Service业务层处理
 * 
 * @author zsn
 * @date 2025-05-29
 */
@Service
@Slf4j
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService
{
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    /**
     * 查询报警规则
     * 
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id)
    {
        return getById(id);
    }

    /**
     * 查询报警规则列表
     * 
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule)
    {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule)
    {
        return save(alertRule) ? 1 : 0;
    }

    /**
     * 修改报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule)
    {
        return updateById(alertRule) ? 1 : 0;
    }

    /**
     * 批量删除报警规则
     * 
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids)
    {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除报警规则信息
     * 
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id)
    {
        return removeById(id) ? 1 : 0;
    }

    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private DeviceMapper deviceMapper;
    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;
    @Autowired
    private IAlertDataService alertDataService;
    @Autowired
    private WebSocketServer webSocketServer;
    @Value("${alertRule.roleAdmin}")
    private String roleAdmin;
    @Value("${alertRule.roleRepair}")
    private String roleRepair;
    /**
     * 上报规则过滤
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void alertFilter() {
        //1.查询规则
        Long ruleCount = this.lambdaQuery().eq(AlertRule::getStatus, 1).count();
        if (ruleCount<=0){
            log.error("[上报规则过滤]:规则表中数据为空,无需处理");
            return;
        }
        //2.规则非空的情况查询所有设备上报的数据,使用缓存
        List<Object> values = redisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA);
        if (CollUtil.isEmpty(values)){
            log.error("[上报规则过滤]:缓存中数据为空,无需处理");
            return;
        }
        //3.数据非空,格式化处理
        List<DeviceData> allDeviceDataList = new ArrayList<>();
        values.forEach(x->{
            List<DeviceData> deviceDataList = JSONUtil.toList(String.valueOf(x), DeviceData.class);
            allDeviceDataList.addAll(deviceDataList);
        });
        //4.格式化之后,遍历所有设备上的上报数据
        for (DeviceData deviceData : allDeviceDataList) {
            //4.1校验设备上报时间是否太久
            int limit = 6000;//单位秒
            long between = LocalDateTimeUtil.between(deviceData.getAlarmTime(), LocalDateTime.now(), ChronoUnit.SECONDS);
            if (between>6000){
                log.info("[上报规则过滤]:设备上报时间过久,无需处理");
                continue;
            }
            //4.2查询上报数据对应的设备绑定的所有报警规则
            //4.2.1查询上报数据对应的设备绑定的产品的报警规则,就是全部设备绑定的意思
            List<AlertRule> productAlertRuleList = this.lambdaQuery().eq(AlertRule::getProductKey, deviceData.getProductKey())
                    .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                    .eq(AlertRule::getIotId, -1)
                    .eq(AlertRule::getStatus, 1).list();
            if (CollUtil.isEmpty(productAlertRuleList)){
                productAlertRuleList = new ArrayList<>();
            }
            //4.2.2查询上报数据对应的设备绑定的报警规则,单个设备的意思,但是规则不一定是单个,因为可以多个规则一台设备
            List<AlertRule> deviceAlertRuleList = this.lambdaQuery().eq(AlertRule::getProductKey, deviceData.getProductKey())
                    .eq(AlertRule::getFunctionId, deviceData.getFunctionId())
                    .eq(AlertRule::getIotId, deviceData.getIotId())
                    .eq(AlertRule::getStatus, 1).list();
            if (CollUtil.isEmpty(deviceAlertRuleList)){
                deviceAlertRuleList = new ArrayList<>();
            }
            //4.2.3合并这些规则
            productAlertRuleList.addAll(deviceAlertRuleList);
            //4.2.4再次判断,规则列表是否为空
            if (CollUtil.isEmpty(productAlertRuleList)){
                log.error("[上报规则过滤]:上报规则为空,无需处理");
                continue;
            }
            //4.2.5对重复规则进行去重
            //获得所有上报规则
            List<AlertRule> alertAllRuleList = CollUtil.distinct(productAlertRuleList);
//            List<AlertRule> alertAllRuleList = productAlertRuleList.stream().distinct().collect(Collectors.toList());

            for (AlertRule alertRule : alertAllRuleList) {
                //5.1:第一层,判断生效时间
                String period = alertRule.getAlertEffectivePeriod();//获取时间
                String[] split = period.split("~");//获取切割后的时间数组
                LocalTime localTime = deviceData.getAlarmTime().toLocalTime();//上报时间,转为LocalTime
                if (localTime.isBefore(LocalTime.parse(split[0])) || localTime.isAfter(LocalTime.parse(split[1]))){
                    log.error("[上报规则过滤]:上报时间不在生效时间内");
                    continue;
                }
                //5.2第二层校验,判断数据阈值
                Double value = alertRule.getValue();//获取阈值
                String operator = alertRule.getOperator();//获取操作符
                Double dateValue = Double.valueOf(deviceData.getDataValue());
                int compare = NumberUtil.compare(dateValue, value);
                if ((compare>=0 && operator.equals(">="))||(compare<0 && operator.equals("<"))){
                    //5.3第三层校验,判断沉默周期,存在,直接结束
                    String silentKey = CharSequenceUtil.format(CacheConstants.IOT_ALERT_SILENT_PREFIX, deviceData.getIotId(), deviceData.getFunctionId(), alertRule.getId());
                    String silentVal = redisTemplate.opsForValue().get(silentKey);
                    if (StringUtils.isNotBlank(silentVal)){
                        log.error("[上报规则过滤]:沉默周期未结束,无需处理");
                        continue;
                    }else {
                        //5.4第四层校验,判断持久周期次数是否达到,没达到直接结束
                        String countKey = CharSequenceUtil.format(CacheConstants.IOT_ALERT_COUNT_PREFIX, deviceData.getIotId(), deviceData.getFunctionId(), alertRule.getId());
                        Long increment = redisTemplate.opsForValue().increment(countKey);//在redis中对键进行自增操作
                        if (increment<alertRule.getDuration()){
                            log.error("[上报规则过滤]:持久周期次数未达到,无需处理");
                            continue;
                        }
                        //保持沉默周期的值到redis,过期时间为沉默周期对应的秒数
                        redisTemplate.opsForValue().setIfAbsent(silentKey,"123",alertRule.getAlertSilentPeriod()*60, TimeUnit.SECONDS);

                        //删除redis中缓存的数据
                        redisTemplate.delete(countKey);

                        //查询老人的护理人员
                        List<Long> nursingIds = null;

                        //如果是老人异常数据,则查询对应护理人员
                        if (alertRule.getAlertDataType()==0){
                            //如果设备是移动设备,直接查护理人员
                            if (deviceData.getLocationType()==0){
                               nursingIds = deviceMapper.selectNursingIdsByIotIdWithElder(deviceData.getIotId());
                            }else {
                                //如果设备是固定设备,关联床位查询老人对应的护理人员
                               nursingIds = deviceMapper.selectNursingIdsByIotIdWithBed(deviceData.getIotId());
                            }
                        }else {
                            //如果是设备数据异常,查询维修工
                            nursingIds = sysUserRoleMapper.selectUserIdsByRoleName(roleRepair);

                        }

                        //查询超级管理员
                        List<Long> adminUserIds = sysUserRoleMapper.selectUserIdsByRoleName(roleAdmin);

                        //合并用户id
                        nursingIds.addAll(adminUserIds);
                        //去重用户id(等待查看异常报警的所有用户id)
                        List<Long> nursingIdList = CollUtil.distinct(nursingIds);
                        //封装报警基础数据对象
                        AlertData alertData = new AlertData();
                        BeanUtils.copyProperties(deviceData,alertData);
                        alertData.setId(null);
                        alertData.setType(alertRule.getAlertDataType());//设备数据类型,0-老人异常数据 1-设备异常数据
                        alertData.setAlertRuleId(alertRule.getId());//规则id
                        alertData.setStatus(0);//0-待处理 1-已处理
                        String reasonTemplate = "功能{}{}{},持续了{}哥周期才报警,沉默周期:{}";
                        String alertReason = CharSequenceUtil.format(reasonTemplate
                                , alertRule.getFunctionId()
                                , alertRule.getOperator()
                                , alertRule.getValue()
                                , alertRule.getDuration()
                                , alertRule.getAlertSilentPeriod()
                        );
                        alertData.setAlertReason(alertReason);//报错原因
                        //所有用户报警数据的集合
                        List<AlertData> alertDataList = new ArrayList<>();
                        for (Long userId : nursingIdList) {
                            //封装用户报警数据,设置用户id
                            AlertData userAlerData = new AlertData();
                            BeanUtils.copyProperties(alertData,userAlerData);
                            userAlerData.setUserId(userId);
                            alertDataList.add(userAlerData);
                        }
                        //批量保存所有用户报警数据
                        boolean result = alertDataService.saveBatch(alertDataList);
                        if (!result){
                            throw new BaseException("保存用户数据失败");
                        }

                        alertDataList.forEach(x->{
                            AlertNotifyVo notifyVo = new AlertNotifyVo();
                            BeanUtils.copyProperties(x,notifyVo);
                            notifyVo.setIsAllConsumer(false);
                            notifyVo.setNotifyType(1);
                            notifyVo.setAlertDataType(alertRule.getAlertDataType());
                            notifyVo.setFunctionName(alertRule.getFunctionName());
                            notifyVo.setAccessLocation(alertRule.getRemark());

                            //发送完毕消息
                            webSocketServer.notifyMsgToClient(x.getUserId().toString(),JSON.toJSONString(notifyVo));
                        });
                    }
                }else {
                    log.error("[上报规则过滤]:数据上报的值没有达到规则阈值,无需处理");
                    continue;
                }
            }
        }
    }



}
